#!/usr/bin/perl
#!/usr/local/bin/perl
######################################
# sysinfo_record.pl: created by Lihy #
######################################
# The program collects the cpu info from "/proc/stat" with fixed frequence,
# it records these infos in files called "sysinfo_<year>-<mon>-<day>-<hour>.rec<num>".
# the <num> indicates the serial num of the record, which means it changes the file to record
# from time to time to avoid huge single file.
# The most interesting thing is the "pcpu.snapshot" file. If it found the total cpu used usage above
# 50% (threshold set by user), it records the most cpu posseded processes into the snapshot using "ps".

use strict;
use warnings;
use List::Util qw(sum);

our $CPUINFO = "/proc/stat";
our $SNAPSHOT = "pcpu.snapshot";
our $cpu_threshold = 50; # Cpu used threshold, above is an alarm

sub collect_once
{
  my @cpuusage;
  my ($sec,$min,$hour) = localtime;
  my $curtime = sprintf ("%d:%d:%d",$hour,$min,$sec);
  open INFILE, "<", $CPUINFO 
    or die "$0:Can't Open CPUINFO,$!";
  if(defined($_=<INFILE>) and /\Acpu\s/) {
    (undef,@cpuusage) = split(/\s+/);
  }
  close(INFILE);
  return ($curtime,@cpuusage);
}

# Specify the file to record with redirecting from STDOUT
sub redirect_bytime
{
  my $num = $_[0];
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
  $year += 1900;
  $mon += 1;
  my $sysinfo_record = sprintf ("sysinfo_%d-%d-%d-%d.rec%d",
                                         $year,$mon,$mday,$hour,$num);
  open STDOUT, ">", $sysinfo_record
    or die "$0: Couldn't Redirect STDOUT,$!";
  print " curtime \tused\tuser\tsys\tiowait\n";
}

# Collect curtime and cpu <used, user, sys, iowait> usage(in percentage)
# every $period sec for $timeout times.
sub collect
{
  my ($timeout,$period) = ($_[0],$_[1]);
  my $counter = 0;
  
  # Get the first cpu info for later subtraction
  my (undef,@last_cpuusage) = &collect_once;
  while (1) {
    my ($curtime,@cpuusage) = &collect_once;
    if (!@cpuusage) {next;}
    my @d_cpuusage = map{$cpuusage[$_]-$last_cpuusage[$_]}0 .. $#cpuusage;
    my $sum = sum(@d_cpuusage);
    if ($sum != 0) {
      @d_cpuusage = map{$d_cpuusage[$_]*100/$sum}0 .. $#d_cpuusage; # Convert to percentage
      my $used = 100-$d_cpuusage[3]; # cpu used time = 100% -idle
      my $user = $d_cpuusage[0];
      my $sys = $d_cpuusage[2];
      my $iowait = $d_cpuusage[4];
      if ($used > $cpu_threshold) {
        printf "!%-8s\t%2.1f\t%2.1f\t%2.1f\t%2.1f\n", $curtime ,$used, $user, $sys,$iowait;
        # write the first 3 process with most cpu usage to pcpu.snapshot using "ps"
        open OUTFILE, ">>", $SNAPSHOT or warn "$0: Couldn't Open Snapshot to Write,$!";
        my @pcpu_snapshot = readpipe("ps -eo pcpu,pid,user,comm --sort -pcpu |head -n 4");
        print OUTFILE "!!!!!! time = $curtime !!!!!!\n";
        print OUTFILE "@pcpu_snapshot\n";
        close OUTFILE;
      } else {
        printf " %-8s\t%2.1f\t%2.1f\t%2.1f\t%2.1f\n", $curtime ,$used, $user, $sys,$iowait;
      }
    }
    @last_cpuusage = @cpuusage;
    if (++$counter > $timeout) {last;}  # Time is out, stop record
    sleep($period);
  }
}

my $num = 0;         # Indicate the serial number of record
my $timeout = 60*60; # Record $timeout times of info in a sinle record file (set by user)
my $period = 2;      # The period for collecting (set by user)
while(1) {
  &redirect_bytime($num);
  &collect($timeout,$period);
  ++$num;
}

